#include "pch.h"
#include "Bot.h"

#include "DataTableSchema.hpp"
#include "UrlTableSchema.hpp"




Bot::Bot(services::provider::const_ptr provider, db::Connection::Ptr connection)
	: m_logger(provider->service<logger::stream>())
	, m_connection(connection)
	, m_coreDataProvider(new DatabaseDataProvider(connection, dataTable(connection)))
	, m_coreUrlProvider(new DatabaseUrlProvider(connection, urlTable(connection)))

	, m_crawler(provider, m_coreDataProvider, m_coreUrlProvider, "Experimental Crawler 0.1a", 20)
	, m_scheduler(provider)
	, m_coreProcessor(new CoreDataProcessor)

	, m_startTime(QTime::currentTime())
{
	loadPlugins();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

Bot::~Bot()
{}
///////////////////////////////////////////////////////////////////////////////////////////////////




Bot::Plugins Bot::plugins() const
{
	return m_plugins;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Bot::add(DataProcessor::Ptr processor)
{
	CoreDataProcessor::Ptr coreProcessor = boost::shared_dynamic_cast<CoreDataProcessor>(processor);

	if(coreProcessor)
		// The core processor receives a UrlProvider as well, because it feeds the
		// crawler
		coreProcessor->attach(m_coreUrlProvider);

	// Give the processor it's own provider
	processor->attach(DataReader::Ptr(new DatabaseDataProvider(m_connection, DataTableSchema::table())));

	m_scheduler.add(processor);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void Bot::add(QUrl url)
{
	m_coreUrlProvider->insert(url);
}
///////////////////////////////////////////////////////////////////////////////////////////////////






const QTime &Bot::startTime() const
{
	return m_startTime;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




QList<QUrl> Bot::activeRequests()
{
	return m_crawler.activeRequests();
}
///////////////////////////////////////////////////////////////////////////////////////////////////




quint64 Bot::numCompletedRequests() const
{
	return m_crawler.numCompletedRequests();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

quint64 Bot::numFilteredReplies() const
{
	return m_crawler.numFilteredReplies();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

int Bot::numActiveRequests() const
{
	return m_crawler.numActiveRequests();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

int Bot::numPendingUrls() const
{
	//return m_urlQueue.sizeHint();
	return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




quint64 Bot::numFilteredUrls() const
{
	return 0;
}
///////////////////////////////////////////////////////////////////////////////////////////////////




float Bot::requestRate() const
{
	return m_crawler.requestRate();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

float Bot::downloadRate() const
{
	return m_crawler.downloadRate();
}
///////////////////////////////////////////////////////////////////////////////////////////////////

float Bot::uploadRate() const
{
	return m_crawler.uploadRate();
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void Bot::loadPlugins()
{
	QCoreApplication *theApp = QCoreApplication::instance();
	QDir pluginsDir = QDir(theApp->applicationDirPath());
	pluginsDir.cd("Plugins");

	QStringList filter;
	filter << "*.dll";
	QStringList dllFiles = pluginsDir.entryList(filter, QDir::Files);

	m_logger->log_info(QString("Found %1 modules in plugin-dir(%2), trying to load them...").arg(dllFiles.size()).arg(pluginsDir.absolutePath()), MLD_MODULE);

	foreach(const QString &fileName, dllFiles)
	{
		try
		{
			PluginLoader::Ptr plugin(new PluginLoader(pluginsDir.absoluteFilePath(fileName)));

			m_plugins << plugin;
			foreach(Plugin::Ptr interface, *plugin)
			{
				// Every plugin may implement several interfaces at once
				DataProcessor::Ptr processor = boost::shared_dynamic_cast<DataProcessor>(interface);
				DataFilter::Ptr filter = boost::shared_dynamic_cast<DataFilter>(interface);
				if(processor)   m_scheduler.add(processor);
				if(filter)      m_crawler.add(filter);
			}
		}
		catch(plugin_not_loaded& e)
		{
			// This exception is thrown when the plugin cannot be loaded
			// (Probably because of an of the idiot who wrote the plugin)
			m_logger->log_error(e.what(), qPrintable(fileName));
		}
		catch(boostext::exception &e)
		{
			// This exception is my problem and thus is logged as an exception
			m_logger->log_detailed(logger::entry::exception, e.what(), MLD_MODULE, e.file(), e.function(), e.line());
			
		}
		catch(std::exception& e)
		{
			// This exception is my problem and thus is logged as an exception
			m_logger->log_detailed(logger::entry::exception, e.what(), MLD_MODULE);
		}
	}

	// Last but not least, add the core data processor
	// to the scheduler
	m_scheduler.add(DataProcessor::Ptr(m_coreProcessor));
}
///////////////////////////////////////////////////////////////////////////////////////////////////




db::TableDL::const_Ptr Bot::dataTable(db::Connection::Ptr connection)
{
	db::TableDL::Ptr table = DataTableSchema::table();
	if(table->exists(connection))
		table->drop(connection);
	table->create(connection);

	return table;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

db::TableDL::const_Ptr Bot::urlTable(db::Connection::Ptr connection)
{
	db::TableDL::Ptr table = UrlTableSchema::table();
	if(table->exists(connection))
		table->drop(connection);
	table->create(connection);

	return table;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
